package com.ruoyi.framework.config;

import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.audit.AuditManager;
import com.mybatisflex.core.audit.AuditMessage;
import com.mybatisflex.solon.MyBatisFlexCustomizer;
import com.ruoyi.common.web.domain.BaseEntity;
import com.ruoyi.framework.impl.DomainInsertListenerImpl;
import com.ruoyi.framework.impl.DomainUpdateListenerImpl;
import com.ruoyi.system.domain.SysSqlLog;
import com.ruoyi.system.service.ISysSqlLogService;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.annotation.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Mybatis-Flex初始化配置
 * @author ruoyi
 */
@Configuration
public class MybatisFlexConfig implements MyBatisFlexCustomizer {

    private static final Logger log = LoggerFactory.getLogger("mybatis-flex-sql");

    @Inject
    SqlLogConfig sqlLogConfig;

    @Inject
    ISysSqlLogService sysSqlLogService;

    @Override
    public void customize(FlexGlobalConfig flexGlobalConfig) {
        globalConfig(flexGlobalConfig);
        sqlLog();
    }

    /**
     * 全局配置
     */
    private void globalConfig(FlexGlobalConfig flexGlobalConfig) {
        // 注册 insertListener
        flexGlobalConfig.registerInsertListener(new DomainInsertListenerImpl<>(), BaseEntity.class);
        // 注册 updateListener
        flexGlobalConfig.registerUpdateListener(new DomainUpdateListenerImpl<>(), BaseEntity.class);
    }

    /**
     * SQL打印
     */
    private void sqlLog() {
        // 如果需要打印sql到日志 或者 开启了审计，则开启 Mybatis-Flex的审计功能
        AuditManager.setAuditEnable(sqlLogConfig.getPrintLog() || sqlLogConfig.getEnabled());

        // 如果是实时模式
        if("2".equals(sqlLogConfig.getMode())) {
            // 设置 SQL实时审计收集器
            AuditManager.setMessageCollector(auditMessage -> handleMessages(Collections.singletonList(auditMessage)));
        } else {
            // 设置 SQL定时审计收集器（每10s收集一次sql执行日志）
            AuditManager.setMessageReporter(auditMessages -> handleMessages(auditMessages));
        }
    }

    /**
     * 处理收集到的sql执行日志
     * @param messages
     */
    private void handleMessages(List<AuditMessage> messages) {
        List<SysSqlLog> list = new ArrayList<>();
        for (AuditMessage auditMessage : messages) {
            // 如果执行超时，记录（排除SQL审计本身的插入语句）
            if(sqlLogConfig.getEnabled()
                    && auditMessage.getElapsedTime() >= sqlLogConfig.getTime()
                    && (!auditMessage.getQuery().contains("INSERT") && !auditMessage.getQuery().contains("sys_sql_log"))) {
                SysSqlLog ssl = new SysSqlLog();
                ssl.setPlatform(auditMessage.getPlatform());
                ssl.setHostIp(auditMessage.getHostIp());
                ssl.setQuery(auditMessage.getQuery());
                ssl.setQueryParams(auditMessage.getQueryParams() != null ? auditMessage.getQueryParams().toString() : null);
                ssl.setQueryTime(Instant.ofEpochMilli(auditMessage.getQueryTime()).atZone(ZoneOffset.systemDefault()).toLocalDateTime());
                ssl.setElapsedTime(auditMessage.getElapsedTime());
                ssl.setDsName(auditMessage.getDsName());
                list.add(ssl);
            }
            // 如果需要印sql到日志
            if(sqlLogConfig.getPrintLog()) {
                log.info("SQL:【{}】, 耗时:【{}ms】", auditMessage.getFullSql(), auditMessage.getElapsedTime());
            }
        }
        if(!list.isEmpty()) {
            sysSqlLogService.saveBatch(list);
        }
    }

}
